package com.sishu.njrtsdms.service.system;

import com.sishu.njrtsdms.constant.Constants;
import com.sishu.njrtsdms.core.RedisCache;
import com.sishu.njrtsdms.domain.core.LoginUser;
import com.sishu.njrtsdms.enums.CacheKeyEnum;
import com.sishu.njrtsdms.exception.ServiceException;
import com.sishu.njrtsdms.exception.user.CaptchaException;
import com.sishu.njrtsdms.exception.user.CaptchaExpireException;
import com.sishu.njrtsdms.exception.user.UserPasswordNotMatchException;
import com.sishu.njrtsdms.jooq.codegen.tables.pojos.User;
import com.sishu.njrtsdms.manager.AsyncManager;
import com.sishu.njrtsdms.manager.factory.AsyncFactory;
import com.sishu.njrtsdms.security.context.AuthenticationContextHolder;
import com.sishu.njrtsdms.service.ConfigService;
import com.sishu.njrtsdms.service.UserService;
import com.sishu.njrtsdms.utils.MessageUtils;
import com.sishu.njrtsdms.utils.ip.IpUtils;

import org.jooq.types.ULong;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 登录校验方法
 *
 * @author njrts-dms
 */
@Component
public class LoginService {
    private final TokenService tokenService;

    private final AuthenticationManager authenticationManager;

    private final RedisCache redisCache;

    private final UserService userService;

    private final ConfigService configService;


    public LoginService(TokenService tokenService,
                        AuthenticationManager authenticationManager,
                        RedisCache redisCache,
                        UserService userService,
                        ConfigService configService) {
        this.tokenService = tokenService;
        this.authenticationManager = authenticationManager;
        this.redisCache = redisCache;
        this.userService = userService;
        this.configService = configService;
    }

    /**
     * 登录验证
     *
     * @param username    用户名
     * @param password    密码
     * @param captcha     验证码
     * @param captchaUUID 验证码唯一标识
     * @return 结果
     */
    public String login(String username,
                        String password,
                        String captcha,
                        String captchaUUID) {
        // 验证码校验
        validateCaptcha(username, captcha, captchaUUID);
        // 用户验证
        Authentication authentication;
        try {
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(
                        AsyncFactory.INSTANCE.recordLoginInfo(
                                username,
                                Constants.LOGIN_FAIL,
                                MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(
                        AsyncFactory.INSTANCE.recordLoginInfo(
                                username,
                                Constants.LOGIN_FAIL,
                                e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.INSTANCE.recordLoginInfo(
                username,
                Constants.LOGIN_SUCCESS,
                MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordUserLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

    /**
     * 校验验证码
     *
     * @param username    用户名
     * @param captcha     验证码
     * @param captchaUUID 唯一标识
     */
    public void validateCaptcha(String username,
                                String captcha,
                                String captchaUUID) {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled) {
            String verifyKey = CacheKeyEnum.CAPTCHA_CODE_KEY.getTypeValue()
                    + StrUtil.nullToEmpty(captchaUUID);
            String redisCaptcha = redisCache.getCacheString(verifyKey);
            redisCache.deleteObject(verifyKey);
            if (redisCaptcha == null) {
                AsyncManager.me().execute(
                        AsyncFactory.INSTANCE.recordLoginInfo(
                                username,
                                Constants.LOGIN_FAIL,
                                MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            if (!captcha.equalsIgnoreCase(redisCaptcha)) {
                AsyncManager.me().execute(
                        AsyncFactory.INSTANCE.recordLoginInfo(
                                username,
                                Constants.LOGIN_FAIL,
                                MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }


    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordUserLoginInfo(Long userId) {
        User user = new User();
        user.setUserId(ULong.valueOf(userId));
        user.setLoginIp(IpUtils.getIpAddr());
        user.setLoginDateTime(LocalDateTimeUtil.now());
        userService.updateUserLoginInfo(user);
    }
}
